use super::{
    err, err_value, json, log_stock, ok, throw_err, update_side, Base, Client, ECode, Lock, MapSv,
    Resp, TBase, VBase, VMap, Value,
};
use tracing::{instrument, warn};

pub struct Stock;

impl Base for Stock {}
impl TBase for Stock {}

impl Stock {
    pub async fn do_cmd(&self, para: &Value, cmd: &str, client: &Client, lock: Lock) -> Resp {
        let data = para.ckobj()?;
        let line = data.ck_str("line")?;

        match cmd {
            "backmt" => self.do_backmt(line, data, client, lock).await,
            "out" => self.do_out(line, data, client, lock).await,
            _ => throw_err!(ECode::NoServe, "no stock serve"),
        }
    }

    #[instrument(skip_all, name = "do_backmt")]
    async fn do_backmt(&self, line: &str, data: &MapSv, client: &Client, lock: Lock) -> Resp {
        let _ = lock.lock().await;

        let sn = data.ck_str("sn")?;
        let back_num = data.ck_i64("num")?;
        let side_mod = data.ck_str("tp")?;
        let mt = data.ck_str("mt")?;
        let pos = match data.k_str("pos") {
            Some(v) => v,
            None => "",
        };

        let mt_mod = self.get_mt_type(mt)?;

        let cmd = json!({
            "line": line,
            "k": sn,
        });

        let body = client.cfgdb(&cmd, &format!("{mt_mod}/get")).await?;
        let stock_data = body.ck_obj("data")?;

        warn!("calc numbers");

        let stock_num = stock_data.ck_i64("dnum")?;
        let mut bnum = match stock_data.k_i64("bnum") {
            Some(v) => v,
            None => 0,
        };

        bnum -= back_num;
        if bnum < 0 {
            bnum = 0;
        }

        let num_left = stock_num + back_num;

        if side_mod == "stock" {
            warn!("update stock");

            let num = stock_data.ck_i64("num")?;

            let cmd = json!({
                "line": line,
                "v": {
                    "sn": sn,
                    "dnum": num_left,
                    "num": num + back_num,
                    "pos": pos,
                }
            });

            let _ = client.cfgdb(&cmd, &format!("{mt_mod}/mditem")).await?;
        } else {
            warn!("update stock");

            let cmd = json!({
                "line": line,
                "v": {
                    "sn": sn,
                    "dnum": num_left,
                    "bnum": bnum,
                    "pos": pos,
                }
            });

            let _ = client.cfgdb(&cmd, &format!("{mt_mod}/mditem")).await?;

            warn!("update side stock");

            let _ = update_side(line, &side_mod, sn, back_num, false, client).await;
        }

        ok!()
    }

    #[instrument(skip_all, name = "do_out")]
    async fn do_out(&self, line: &str, data: &MapSv, client: &Client, lock: Lock) -> Resp {
        let _ = lock.lock().await;

        let pick_sn = data.ck_str("sn")?;
        let mut box_num = match data.k_i64("box") {
            Some(v) => v,
            None => 0,
        };

        let cmd = json!({
            "line": line,
            "k": pick_sn,
        });

        warn!("get pick");

        let body = client.cfgdb(&cmd, "pick/get").await?;
        let pick_data = body.ck_obj("data")?;

        let batch_sn = pick_data.ck_str("bsn")?;
        let mt = pick_data.ck_str("mt")?;
        let side = pick_data.ck_str("side")?;
        let usn = pick_data.ck_str("usn")?;
        let tech = pick_data.ck_str("tech")?;
        let mut pick_num = pick_data.ck_i64("num")?;

        let mt_mod = self.get_mt_type(mt)?;
        let side_mod = self.get_side_type(side)?;

        warn!("get stock");

        let cmd = json!({
            "line": line,
            "k": batch_sn,
        });

        let body = client.cfgdb(&cmd, &format!("{mt_mod}/get")).await?;
        let stock_data = body.ck_obj("data")?;

        warn!("calc numbers");

        let stock_num = stock_data.ck_i64("dnum")?;
        let stock_box = match stock_data.k_i64("box") {
            Some(v) => v,
            None => 0,
        };
        let bnum = match stock_data.k_i64("bnum") {
            Some(v) => v,
            None => 0,
        };

        let mut box_left = stock_box - box_num;
        if box_left < 0 {
            box_left = 0;
            box_num = stock_box;
        }

        if box_num > 0 && mt == "G" {
            let snum = stock_data.ck_i64("snum")?;
            pick_num = snum * box_num;
        }

        let mut num_left = stock_num - pick_num;

        if num_left < 0 {
            num_left = 0;
            pick_num = stock_num;
        }

        warn!("update stock");

        let cmd = json!({
            "line": line,
            "v": {
                "sn": batch_sn,
                "dnum": num_left,
                "box": box_left,
                "bnum": pick_num + bnum,
            }
        });

        let _ = client.cfgdb(&cmd, &format!("{mt_mod}/mditem")).await?;

        warn!("rm pick");

        let cmd = json!({
            "line": line,
            "k": pick_sn,
        });

        let _ = client.cfgdb(&cmd, "pick/rm").await?;

        warn!("update side stock");

        let is_update = update_side(line, &side_mod, batch_sn, pick_num, true, client).await?;
        if !is_update {
            let cmd = json!({
                "line": line,
                "v": {
                    "sn": batch_sn,
                    "num": pick_num,
                    "mt": mt,
                    "tech": tech,
                    "ball": self.str_or_empty(stock_data, "ball"),
                    "std": self.str_or_empty(stock_data, "std"),
                    "sub": self.str_or_empty(stock_data, "sub"),
                }
            });

            let _ = client.cfgdb(&cmd, &format!("{side_mod}/add")).await?;
        }

        warn!("save to outstock for history");

        let mut val = pick_data.clone();
        let k = json!({ "usn": usn });

        let _ = log_stock(line, "hispick", "mes", "mes", &mut val, Some(k), client, "").await;

        ok!(stock_data)
    }
}
